//
// Created by tang on 2022/1/11.
//

#include "integrations/redmine/redmine_apis.hpp"
#include "cpr/cpr.h"
#include "data/app_config.hpp"
#include "utils/app_utils.hpp"
#include "fmt/format.h"

namespace RedMine {
    std::vector<Response::RedMineTimeEntryActivity> GetRedMineActivities() {
        const auto& cfg = Config::AppConfig::Instance();
        std::vector<Response::RedMineTimeEntryActivity> ret{};

        auto url = cfg.Get(Config::KEY_REDMINE_SERVER_URL);
        if (!url.empty()) {
            url = url.append("enumerations/time_entry_activities.json");

            auto res = Get(
                cpr::Url(url),
                cpr::Header{ {"X-RedMine-API-Key", cfg.Get(Config::KEY_REDMINE_API_KEY)} },
                cpr::VerifySsl(false),
                cpr::ConnectTimeout(5000),
                cpr::Timeout(15000)
            );
            if (res.status_code == 200) {
                Response::RedMineActivityListResponse data{};
                auto json = nlohmann::json::parse(res.text);
                json.get_to(data);
                ret = move(data.time_entry_activities);
            }
            else
            {
                SendError(fmt::format("https code: {}\ncurl code: {}\nresponse body: {}", res.status_code, res.error.message, res.text));
            }
        }
        return ret;
    }

    std::vector<Response::RedMineIssue>
        SearchRedMineIssue(int search_type, const std::string& keyword, bool only_me, bool only_open, int page_size, int page, int* total) {
        const auto& cfg = Config::AppConfig::Instance();
        std::vector<Response::RedMineIssue> ret{};

        if (search_type >= SFT_CNT || search_type < 0)
            return ret;

        auto url = cfg.Get(Config::KEY_REDMINE_SERVER_URL);
        if (!url.empty()) {
            url = url.append("issues.json");

            cpr::Parameters parameters{
                {"limit", std::to_string(page_size)},
                {"offset", std::to_string(page * page_size)}
            };

            if (!keyword.empty()) {
                if (search_type == SFT_SUBJECT)
                    parameters.Add({ "subject", "~" + keyword });
                else if (search_type == SFT_ISSUE_ID)
                    parameters.Add({ "issue_id", keyword });
            }
            if (only_me)
                parameters.Add({ "assigned_to_id", "me" });
            if (only_open)
                parameters.Add({ "status_id","open" });
            else
                parameters.Add({ "status_id","*" });

            auto res = Get(
                cpr::Url(url),
                cpr::Header{ {"X-RedMine-API-Key", cfg.Get(Config::KEY_REDMINE_API_KEY)} },
                parameters,
                cpr::VerifySsl(false),
                cpr::ConnectTimeout(5000),
                cpr::Timeout(15000)
            );
            if (res.status_code == 200) {
                Response::RedMineIssueSearchResponse data{};
                auto json = nlohmann::json::parse(res.text);
                json.get_to(data);
                if (total)
                    *total = data.total_count;
                ret = move(data.issues);
            }
            else
            {
                SendError(fmt::format("https code: {}\ncurl code: {}\nresponse body: {}", res.status_code, res.error.message, res.text));
            }
        }
        return ret;
    }

    Response::UserInfo GetCurrentUserInfo()
    {
        const auto& cfg = Config::AppConfig::Instance();
        Response::UserInfo ret{ .id = 0 };

        auto url = cfg.Get(Config::KEY_REDMINE_SERVER_URL);
        if (!url.empty()) {
            url = url.append("users/current.json");

            auto res = Get(
                cpr::Url(url),
                cpr::Header{ {"X-RedMine-API-Key", cfg.Get(Config::KEY_REDMINE_API_KEY)} },
                cpr::VerifySsl(false),
                cpr::ConnectTimeout(5000),
                cpr::Timeout(15000)
            );
            if (res.status_code == 200) {
                auto json = nlohmann::json::parse(res.text);
                if (json.contains("user"))
                {
                    ret = json.value<Response::UserInfo>("user", ret);
                }
                else
                {
                    SendError(res.text);
                }
            }
            else
            {
                SendError(fmt::format("https code: {}\ncurl code: {}\nresponse body: {}", res.status_code, res.error.message, res.text));
            }
        }
        return ret;
    }

    Response::ProjectInfo GetIssueProjectInfo(int issue_id)
    {
        Response::ProjectInfo info;
        auto all = SearchRedMineIssue(SFT_ISSUE_ID, std::to_string(issue_id), false, false, 10, 0, nullptr);
        if (all.size() == 1) {
             const auto& cfg = Config::AppConfig::Instance();
            auto url = cfg.Get(Config::KEY_REDMINE_SERVER_URL);
            if (!url.empty()) {
                url = url.append("projects/" + std::to_string(all.front().project.id) + ".json");

                auto res = Get(
                    cpr::Url(url),
                    cpr::Header{ {"X-RedMine-API-Key", cfg.Get(Config::KEY_REDMINE_API_KEY)} },
                    cpr::VerifySsl(false),
                    cpr::ConnectTimeout(5000),
                    cpr::Timeout(15000)
                );
                if (res.status_code == 200) {
                    auto json = nlohmann::json::parse(res.text);
                    if (json.contains("project"))
                    {
                        info = json.value<Response::ProjectInfo>("project", info);
                    }
                    else
                    {
                        SendError(res.text);
                    }
                }
                else
                {
                    SendError(fmt::format("https code: {}\ncurl code: {}\nresponse body: {}", res.status_code, res.error.message, res.text));
                }
            }
        }
        return info;
    }

    bool CreateTimeEntry(int issue_id, const std::string& date, double hours, int activity, const std::string& comment)
    {
        const auto& cfg = Config::AppConfig::Instance();
        auto url = cfg.Get(Config::KEY_REDMINE_SERVER_URL);
        if (!url.empty()) {
            url = url.append("time_entries.json");

            nlohmann::json entry;
            entry["issue_id"] = issue_id;
            entry["spent_on"] = date;
            entry["hours"] = hours;
            entry["activity_id"] = activity;
            entry["comments"] = comment;

            nlohmann::json body;
            body["time_entry"] = entry;

            auto res = Post(cpr::Url(url),
                cpr::Header{
                    {"X-RedMine-API-Key", cfg.Get(Config::KEY_REDMINE_API_KEY)},
                    {"Content-Type", "application/json"} },
                    cpr::Body(body.dump()),
                    cpr::VerifySsl(false),
                    cpr::ConnectTimeout(5000),
                    cpr::Timeout(15000));

            return res.status_code == 201; // 201: Created
        }
        return false;
    }

    std::string GetIssueProjectDesc(int issue_id)
    {
        auto info = GetIssueProjectInfo(issue_id);
        return info.description;
    }
}
